package island;

public final class PerlinNoise {
	
	private static final float frequency = 0.5f;
	private static final float persistence = 64f;
	
//	public PerlinNoise(float newFrequency, float newPersistence, int newOctaves) {
//		frequency = newFrequency;
//		persistence = newPersistence;
//		octaves = newOctaves;
//	}
	
	private static float cosineInterpolate(float a, float b, float x) {
		float ft = x * 3.1415927f;
		float f = (1 - (float) Math.cos(ft)) * 0.5f;
		return a * (1 - f) + b * f;
	}
	
	/*
	 * 1D NOISE
	 */
	
	private static float noise1D(float x) {
		int n = (int) x;
		n = (n << 13) ^ n;
		return (1.0f - ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f);
	}
	
	private static float smoothedNoise1D(float x) {
		return noise1D(x)/2  +  noise1D(x-1)/4  +  noise1D(x+1)/4;
	}
	
	private static float interpolatedNoise1D(float x) {
		int intX = (int) x;
		float fractionalX = x - intX;

	    float v1 = smoothedNoise1D(intX);
		float v2 = smoothedNoise1D(intX + 1);

		return cosineInterpolate(v1, v2, fractionalX);
	}
	
	public static float get1D(int octaves, float x) {
		float total = 0;

		for (int i = 0; i < octaves; i++) {
			float freq = frequency * i;
			float amplitude = persistence * i;
			total += interpolatedNoise1D(x * freq) * amplitude;
		}

		return total;
	}
	
	/*
	 * 2D NOISE
	 */
	
	public static float noise2D(float x, float y) {
		int n = (int) x + (int) y * 57;
		n = (n << 13) ^ n;
		return (1.0f - ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f);
	}
	
	private static float smoothedNoise2D(float x, float y) {
		float corners = (noise2D(x - 1, y - 1) + noise2D(x + 1, y - 1) + noise2D(x - 1, y + 1) + noise2D(x + 1, y + 1)) / 16;
		float sides = (noise2D(x - 1, y) + noise2D(x + 1, y) + noise2D(x, y - 1) + noise2D(x, y + 1)) / 8;
		float center = noise2D(x, y) / 4;
		return corners + sides + center;
	}
	
	private static float interpolatedNoise2D(float x, float y) {
		int intX = (int) x;
		float fractionalX = x - intX;

		int intY = (int) y;
		float fractionalY = y - intY;

		float v1 = smoothedNoise2D(intX, intY);
		float v2 = smoothedNoise2D(intX + 1, intY);
		float v3 = smoothedNoise2D(intX, intY + 1);
		float v4 = smoothedNoise2D(intX + 1, intY + 1);
		float i1 = cosineInterpolate(v1, v2, fractionalX);
		float i2 = cosineInterpolate(v3, v4, fractionalX);

		return cosineInterpolate(i1, i2, fractionalY);
	}
	
	public static float get2D(int octaves, float x, float y) {
		float total = 0;
		float freq = 0;
		float amplitude = 0;
		
		for (int i = 0; i < octaves; i++) {
			freq = 2 << i;
			amplitude = (float) Math.pow(persistence, i);
			total += interpolatedNoise2D(x * freq, y * freq) * amplitude;
		}

		return total;
	}
}